﻿using Fine.CRM.Common;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;


namespace Fine.CRM.Common
{
    class SequenceNumbers
    {
        private IOrganizationService orgService;
       
        public SequenceNumbers(IOrganizationService orgService)
        {
            this.orgService = orgService;
        }

        public string GetSequenceNumber(string SequenceNumberName)
        {
            Entity record = getSequencyNumberEntityByName(SequenceNumberName);

            //if a matching record was not found then create a default record now.
            if (record == null)
            {
                record = this.createDefaultSequenceNumber(SequenceNumberName);
            }

            //lock the record by updating the dummy lock field. This prevents another thread from
            //modifying the squence value and while this thread is is modifying the record.
            Guid lockValue = Guid.NewGuid();
            Entity lockRecord = new Entity("fine_sequencenumber");
            lockRecord.Id = record.Id;
            lockRecord.Attributes.Add("fine_dummylock",lockValue.ToString());
            orgService.Update(lockRecord);
            lockRecord = null;

            //retrieve the record again because it could have changed prior to the lock
            record = orgService.Retrieve(record.LogicalName, record.Id, new ColumnSet(true));

            //get the current sequence number
            int value = record.GetAttributeValue<int>("fine_value");

            //setup the prefix for the number
            string prefix = record.GetValue<string>("fine_prefix", string.Empty);
            
            //setup the padding format for the sequence number
            int paddingLength = record.GetValue<int>("fine_paddinglength",1);
            string numberFormat = "D" + paddingLength.ToString();

            //increment the sequence number for the next record.
            int incrementValue = record.GetValue<int>("fine_incrementvalue",1);
            int updateValue = value + incrementValue;

            lockRecord = new Entity("fine_sequencenumber");
            lockRecord.Id = record.Id;
            lockRecord.Attributes.Add("fine_value",updateValue);
            orgService.Update(lockRecord);

            //create the return value string based on the prefix, value and format;
            string returnValue = prefix + value.ToString(numberFormat);

            return returnValue;

        }

        private Entity getSequencyNumberEntityByName(string SequenceNumberName)
        {
            //Query by attribute to get the sequence number that matches the name
            Entity returnValue = null;

            QueryExpression qry = new QueryExpression("fine_sequencenumber");
            qry.ColumnSet = new ColumnSet(true);
            qry.Criteria = new FilterExpression(LogicalOperator.And);
            qry.Criteria.AddCondition(new ConditionExpression("fine_name", ConditionOperator.Equal, SequenceNumberName));
            qry.Criteria.AddCondition(new ConditionExpression("statecode", ConditionOperator.Equal, 0));

            EntityCollection qryResult = orgService.RetrieveMultiple(qry);
            if (qryResult.Entities.Count == 1)
            {
                returnValue = qryResult.Entities[0];
            }

            return returnValue;

        }

        private Entity createDefaultSequenceNumber(string SequenceNumberName)
        {
            Entity newSequence = new Entity("fine_sequencenumber");
            newSequence.Attributes.Add("fine_name",SequenceNumberName);
            newSequence.Attributes.Add("fine_value",1);
            Guid id = this.orgService.Create(newSequence);
            return this.orgService.Retrieve("fine_sequencenumber", id, new ColumnSet(true));
        }

    }
}

